﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright company="Microsoft Corp." file="InitializeSqlServerOperation.cs">
//   All rights reserved. See License.txt in the project root for license information.
// </copyright>
// 
// --------------------------------------------------------------------------------------------------------------------
namespace CloudBlitz.DeploymentManager.DeploymentOperations
{
    using System;
    using System.Linq;

    using CloudBlitz.DeploymentManager.DeploymentWorkflow;

    public class InitializeSqlServerOperation : DeploymentOperationBase
    {
        private readonly IAzureServiceClientsFactory azureServiceClientsFactory;

        private readonly Func<SqlAzureServerCredentials, ISqlAzureServerManager> sqlAzureServerManagerFactory;

        public InitializeSqlServerOperation(
            IAzureServiceClientsFactory azureServiceClientsFactory, 
            Func<SqlAzureServerCredentials, ISqlAzureServerManager> sqlAzureServerManagerFactory) : base(2)
        {
            this.azureServiceClientsFactory = azureServiceClientsFactory;
            this.sqlAzureServerManagerFactory = sqlAzureServerManagerFactory;
        }

        protected override OperationResult ExecuteOverride(DeploymentContext context)
        {
            var sqlAzureManagementClient =
                this.azureServiceClientsFactory.CreateSqlAzureManagementClient(context.AzureCredentials);

            try
            {
                this.RaiseProgressChangedEvent(0, "Creating SQL server");

                var sqlServerList = sqlAzureManagementClient.ListSqlAzureServers().Result;
                if (!sqlServerList.Any(x => x.Name.Equals(context.SqlServerDetails.Name)))
                {
                    var sqlAzureServerManager = this.sqlAzureServerManagerFactory(context.SqlServerDetails.Credentials);
                    foreach (var sqlAzureServer in sqlServerList.Where(x => x.AdministratorLogin.Equals(context.SqlServerDetails.Credentials.AdministratorLogin)))
                    {
                        try
                        {
                            sqlAzureServerManager.ListDatabases(sqlAzureServer.Name);

                            context.SqlServerDetails.Name = sqlAzureServer.Name;

                            this.RaiseProgressChangedEvent(100, "SQL server found");
                            return OperationResult.Success;
                        }
                        catch
                        {
                        }
                    }
                }
                else
                {
                    this.RaiseProgressChangedEvent(100, string.Format("SQL server \"{0}\" already exists", context.SqlServerDetails.Name));
                    return OperationResult.Success;
                }
                
                context.SqlServerDetails.Name = sqlAzureManagementClient.CreateSqlAzureServer(
                                                        context.SqlServerDetails.Credentials.AdministratorLogin,
                                                        context.SqlServerDetails.Credentials.AdministratorPassword,
                                                        context.Location).Result;

                this.RaiseProgressChangedEvent(60, string.Format("SQL server \"{0}\" has been created, creating firewall rules", context.SqlServerDetails.Name));

                sqlAzureManagementClient.CreateFirewallRule(
                                                context.SqlServerDetails.Name,
                                                "MicrosoftService",
                                                "0.0.0.0",
                                                "0.0.0.0");

                var localIp = sqlAzureManagementClient.CreateFirewallRuleWithIpDetect(
                                                                    context.SqlServerDetails.Name,
                                                                    "AutoDetectIpIpFirewallRule").Result;

                var ipRange = this.GenerateIpRange(localIp);
                sqlAzureManagementClient.CreateFirewallRule(
                                            context.SqlServerDetails.Name,
                                            "IpRangeFirewallRule",
                                            ipRange[0],
                                            ipRange[1]);

                this.RaiseProgressChangedEvent(100, string.Format("Finished initializing SQL server \"{0}\"", context.SqlServerDetails.Name));
                return OperationResult.Success;
            }
            catch (Exception e)
            {
                return ExceptionHelper.HandleException(e);
            }
        }

        /// <summary>
        /// generate an IP range based on IP address of the user's machine. 
        /// due to potential existence of proxy servers between the client and Azure, instead of using
        /// the address we are accessing Azure with currently we generate a range from ***.***.0.0 to 
        /// ***.***.255.255 so the connection from the client to the database can be re-routed to any 
        /// of the proxy servers without the firewall failing the new connection. The range could be smaller
        /// if the addresses of the proxy servers are known.
        /// </summary>
        /// <param name="localIp">IP address return by AzureManagementbroker.CreateFirewallRuleWithIPDetect </param>
        /// <returns>two IPs as shown in the summary</returns>
        private string[] GenerateIpRange(string localIp)
        {
            var ipBytes = localIp.Split('.');

            var ipRange = new string[2];

            ipRange[0] = ipBytes[0] + "." + ipBytes[1] + "." + "0" + "." + "0";
            ipRange[1] = ipBytes[0] + "." + ipBytes[1] + "." + "255" + "." + "255";

            return ipRange;
        }
    }
}